#!/bin/bash
# This script is released under the GNU General Public License 3.0
# Check the COPYING file included with this distribution

# bootloader_grub
# installs traditional Grub
#
# parameters (required)
#  CONFIG_LIST: One string items of defined FSspec
#
# returns 0 on success
# returns $ERROR_CANCEL=64 on user cancel
# anything else is a real error
# reason: show_dialog() needs a way to exit "Cancel"
#
# writes menus and noise to STDERR

# location of other scripts to source
readonly SHAREDIR="$(dirname ${0})" || exit $?

# source bootloader commons
source "${SHAREDIR}"/bootloader_common.sh || exit $?

#########################################
## START: dialog functions/definitions ##

# get_grub_map()
# prints grub device map to "${DESTDIR}/boot/grub/device.map"
#
# parameters (none)
#
get_grub_map() {
	# check input
	check_num_args "${FUNCNAME}" 0 $# || return $?
	[ -e "${DESTDIR}/boot/grub/device.map" ] && rm "${DESTDIR}/boot/grub/device.map"
	show_dialog --infobox "Generating GRUB device map...\nThis could take a while.\n\n Please be patient." 0 0
	${DESTDIR}/sbin/grub --no-floppy --device-map "${DESTDIR}/boot/grub/device.map" >/tmp/grub.log 2>&1 <<EOF
quit
EOF
}

# get_parent_disk()
# Gets the parent disk of a partition, prints to STDOUT
#
# returns 0 on success
#
# parameters (required):
#  _PARTITION: the partition
#
get_parent_disk() {
	# check input
	check_num_args "${FUNCNAME}" 1 $# || return $?
	local _PARENT=
	# get parent (/dev/sda)
	_PARENT="$(lsblk -dnp -o PKNAME "${1}")" || return $?
	# check type='disk'
	if [ "$(lsblk -dnp -o TYPE "${_PARENT}")" != 'disk' ]; then
		echo "ERROR: Expected type=disk!" 1>&2
		return 1
	fi
	echo "${_PARENT}"
	return 0
}

# mapdev()
# maps a partition to a grub device
# uses "${DESTDIR}/boot/grub/device.map" from get_grub_map()
#
# parameters (required)
#  _PARTITION: The partition
#
mapdev() {
	# check input
	check_num_args "${FUNCNAME}" 1 $# || return $?
	local _PARTITION="$1"
	local _DISK=
	local _PNUM=
	local _DISK_GRUB=
	# get parent (/dev/sda)
	_DISK="$(get_parent_disk "${_PARTITION}")" || return $?
	# /dev/sdaXY
	_PNUM="${_PARTITION#"${_DISK}"}"
	# -1 (grub starts counting at 1)
	_PNUM=$((${_PNUM}-1)) || return $?
	_DISK_GRUB="$(sed -r -e 's/[\(\)]//g' -e 's/[[:blank:]]+/ /g' "${DESTDIR}/boot/grub/device.map" | grep " ${_DISK}$" | cut -d' ' -f1)" || return $?
	if [ -n "${_DISK_GRUB}" ] && [ -n "${_PNUM}" ]; then
		echo -n "(${_DISK_GRUB},${_PNUM})"
		return 0
	else
		echo "ERROR: GRUB device not found properly" 1>&2
		return 1
	fi
}

## END: dialog functions/definitions ##
#######################################

#####################
## begin execution ##

# check input
check_num_args "$(basename $0)" 1 $# || exit $?
CONFIG_LIST="${1}"
CONFIG_ITEM=
RET_SUB=

# write grub device map to "${DESTDIR}/boot/grub/device.map"
get_grub_map

# get root partition
CONFIG_ITEM="$("${SHAREDIR}"/FSspec listfind "${CONFIG_LIST}" 'mountpoint' '/')" || exit $?
PART_ROOT="$("${SHAREDIR}"/FSspec parse "${CONFIG_ITEM}" 'partition')" || exit $?
# root partition encrypted?
CRYPTTYPE="$("${SHAREDIR}"/FSspec parse "${CONFIG_ITEM}" 'crypttype')" || exit $?

# look for a separately-mounted /boot partition
if "${SHAREDIR}"/FSspec list_haskeyvalue "${CONFIG_LIST}" 'mountpoint' '/boot'; then
	# get boot partition
	CONFIG_ITEM="$("${SHAREDIR}"/FSspec listfind "${CONFIG_LIST}" 'mountpoint' '/boot')" || exit $?
	PART_BOOT="$("${SHAREDIR}"/FSspec parse "${CONFIG_ITEM}" 'partition')" || exit $?
	PART_GRUB="$(mapdev ${PART_BOOT})" || exit $?
	SUBDIR=
	DISK_BOOT="$(get_parent_disk "${PART_BOOT}")" || exit $?
else
	PART_GRUB="$(mapdev ${PART_ROOT})" || exit $?
	SUBDIR="/boot"
	DISK_BOOT="$(get_parent_disk "${PART_ROOT}")" || exit $?
fi

KERNEL_PARAMS=
# get kernel version
KERNVER="$(getkernelversion)" || exit $?
# get kernel params
KERNEL_PARAMS="$(getkernelparams "${PART_ROOT}" "${PART_BOOT}" "${CRYPTTYPE}")" || exit $?

GRUBMENU="${DESTDIR}/boot/grub/grub.conf"
rm "${DESTDIR}"/boot/grub/menu.lst
# TODO: I dont like this ln
ln -s ./grub.conf "${DESTDIR}/boot/grub/menu.lst"
# sanity check
if [ ! -f "${GRUBMENU}" ]; then
	show_dialog --msgbox "Error: Couldn't find ${GRUBMENU}. Is GRUB installed?" 0 0
	exit 1
fi

# remove default entries
sed -i 's/^#splashimage/splashimage/' "${GRUBMENU}" || exit $?
sed -i '/^#/d' "${GRUBMENU}" || exit $?
# write new grubmenu
cat >>${GRUBMENU} <<EOF

# (0) Pentoo
title  Pentoo
root   ${PART_GRUB}
kernel ${SUBDIR}/kernel-genkernel${KERNVER} ${KERNEL_PARAMS}
initrd ${SUBDIR}/initramfs-genkernel${KERNVER}

# (2) Windows
#title Windows
#rootnoverify (hd0,0)
#makeactive
#chainloader +1
EOF
RET_SUB=$?
[ "${RET_SUB}" -ne 0 ] && exit "${RET_SUB}"

# inform user about target disk, partitions and such, last chance to abort ;)
show_dialog --defaultno --yesno "GRUB bootloader will be installed to '${DISK_BOOT}'.\nPlease confirm this.\n\nYou will then be put into the editor to review the GRUB configuration file.\nInstallation will continue after you exit the editor." 0 0 || exit $?

# set system editor (if not already defined)
chroot_mount || exit $?
EDITOR="$(geteditor)" || exit $?
chroot_umount || exit $?

# let user edit grub menu file
"${EDITOR}" "${GRUBMENU}" || exit $?

show_dialog --infobox "Installing the GRUB bootloader..." 0 0
if [ -d "${DESTDIR}"/usr/lib/grub/i386-pc ]; then
	cp -a "${DESTDIR}"/usr/lib/grub/i386-pc/* "${DESTDIR}"/boot/grub/ || exit $?
fi
sync

# freeze xfs filesystems to enable grub installation on xfs filesystems
if [ -x /usr/sbin/xfs_freeze ]; then
	for MOUNTPOINT_XFS in $(mount | grep " ${DESTDIR}" | grep ' type xfs ' | cut -d' ' -f3); do
		echo "INFO: Freezing XFS filesystem mounted at ${MOUNTPOINT_XFS}" 1>&2
		/usr/sbin/xfs_freeze -f "${MOUNTPOINT_XFS}" 2>/dev/null
	done
fi

/sbin/grub-install --no-floppy --recheck --grub-shell="${DESTDIR}/sbin/grub" --root-directory="${DESTDIR}" "${DISK_BOOT}" >/tmp/grub.log 2>&1
cat /tmp/grub.log >"${LOG}"

# unfreeze xfs filesystems
if [ -x /usr/sbin/xfs_freeze ]; then
	for MOUNTPOINT_XFS in $(mount | grep " ${DESTDIR}" | grep ' type xfs ' | cut -d' ' -f3); do
		echo "INFO: Un-freezing XFS filesystem mounted at ${MOUNTPOINT_XFS}" 1>&2
		/usr/sbin/xfs_freeze -u "${MOUNTPOINT_XFS}" 2>/dev/null
	done
fi

if grep -q 'Error [0-9]*: ' /tmp/grub.log; then
	show_dialog --msgbox "Error installing GRUB. (see ${LOG} for output)" 0 0
	exit 1
fi

show_dialog --msgbox "GRUB was successfully installed." 0 0
exit 0
